{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9af3e9a8",
   "metadata": {},
   "source": [
    "# jupyter notebook 里的前后端通信\n",
    "\n",
    "> https://jupyter-notebook.readthedocs.io/en/stable/comms.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ae0821f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipywidgets import Output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "755a5520",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "58c8b8915be74e51a3e5325d71f44519",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output_1 = Output()\n",
    "output_1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c135821f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id = \"output_1\"></div>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<div id = \"output_1\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9a38cfb2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "Jupyter.notebook.kernel.comm_manager.register_target('my_comm_target',\n",
       "    function(comm, msg) {\n",
       "        console.log(msg)\n",
       "        $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
       "        // comm is the frontend comm instance\n",
       "        // msg is the comm_open message, which can carry data\n",
       "\n",
       "        // Register handlers for later messages:\n",
       "        comm.on_msg(function(msg) {\n",
       "            $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
       "        });\n",
       "        comm.on_close(function(msg) {\n",
       "            $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
       "        });\n",
       "        comm.send({'foo':3});\n",
       "    });\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%js\n",
    "Jupyter.notebook.kernel.comm_manager.register_target('my_comm_target',\n",
    "    function(comm, msg) {\n",
    "        console.log(msg)\n",
    "        $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
    "        // comm is the frontend comm instance\n",
    "        // msg is the comm_open message, which can carry data\n",
    "\n",
    "        // Register handlers for later messages:\n",
    "        comm.on_msg(function(msg) {\n",
    "            $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
    "        });\n",
    "        comm.on_close(function(msg) {\n",
    "            $(\"#output_1\").append(`${msg[\"content\"]['data'].foo}`)\n",
    "        });\n",
    "        comm.send({'foo':3});\n",
    "    });"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c2a1ce85",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipykernel.comm import Comm\n",
    "\n",
    "# Use comm to send a message from the kernel\n",
    "my_comm = Comm(target_name='my_comm_target', data={'foo':0})\n",
    "my_comm.send({'foo':1})\n",
    "\n",
    "# Add a callback for received messages.\n",
    "@my_comm.on_msg\n",
    "def _recv(msg):\n",
    "    with output_1:\n",
    "        my_comm.send({\"foo\":2})\n",
    "        print(msg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e4ae1de8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "016dfd926170445a965634c72700e6df",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output_2 = Output()\n",
    "output_2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "1779ce71",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id = \"output_2\"></div>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<div id = \"output_2\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fbca0671",
   "metadata": {},
   "outputs": [],
   "source": [
    "def target_func(comm, open_msg):\n",
    "    with output_2:\n",
    "        print(open_msg)\n",
    "    # comm is the kernel Comm instance\n",
    "    # msg is the comm_open message\n",
    "\n",
    "    # Register handler for later messages\n",
    "    @comm.on_msg\n",
    "    def _recv(msg):\n",
    "        # Use msg['content']['data'] for the data in the message\n",
    "        with output_2:\n",
    "            print(msg)\n",
    "            comm.send(msg['content']['data'])\n",
    "\n",
    "    # Send data to the frontend on creation\n",
    "    comm.send({'foo': 5})\n",
    "\n",
    "get_ipython().kernel.comm_manager.register_target('my_comm_target', target_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "fc23ca4c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "const comm = Jupyter.notebook.kernel.comm_manager.new_comm('my_comm_target', {'foo': 6})\n",
       "// Send data\n",
       "comm.send({'foo': 7})\n",
       "\n",
       "// Register a handler\n",
       "comm.on_msg(function(msg) {\n",
       "    console.log(msg.content.data.foo);\n",
       "    $(\"#output_2\").append(`${msg[\"content\"]['data'].foo}`)\n",
       "});\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%js\n",
    "const comm = Jupyter.notebook.kernel.comm_manager.new_comm('my_comm_target', {'foo': 6})\n",
    "// Send data\n",
    "comm.send({'foo': 7})\n",
    "\n",
    "// Register a handler\n",
    "comm.on_msg(function(msg) {\n",
    "    console.log(msg.content.data.foo);\n",
    "    $(\"#output_2\").append(`${msg[\"content\"]['data'].foo}`)\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "017e0bf6",
   "metadata": {},
   "source": [
    "# 富文本编辑器\n",
    "\n",
    "> https://www.wangeditor.com/v5/installation.html\n",
    ">\n",
    "> https://ckeditor.com/docs/ckeditor5/latest/installation/getting-started/quick-start.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8b2926f2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<!-- 引入 css -->\n",
       "<link href=\"https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css\" rel=\"stylesheet\">\n",
       "<script>\n",
       "require.config({\n",
       "    paths : {\n",
       "        \"wangEditor\" : 'https://unpkg.com/@wangeditor/editor@latest/dist/index',\n",
       "        'CKEditor': 'https://cdn.ckeditor.com/ckeditor5/34.2.0/classic/ckeditor',\n",
       "    },\n",
       "    shim : {\n",
       "        'wangEditor': {\n",
       "            exports: 'wangEditor'\n",
       "        }\n",
       "    }\n",
       "})\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<!-- 引入 css -->\n",
    "<link href=\"https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css\" rel=\"stylesheet\">\n",
    "<script>\n",
    "require.config({\n",
    "    paths : {\n",
    "        \"wangEditor\" : 'https://unpkg.com/@wangeditor/editor@latest/dist/index',\n",
    "        'CKEditor': 'https://cdn.ckeditor.com/ckeditor5/34.2.0/classic/ckeditor',\n",
    "    },\n",
    "    shim : {\n",
    "        'wangEditor': {\n",
    "            exports: 'wangEditor'\n",
    "        }\n",
    "    }\n",
    "})\n",
    "</script>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "7edf031f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id=\"toolbar-container\"></div>\n",
       "<div id=\"editor-container\"></div>\n",
       "<script>\n",
       "requirejs(['wangEditor'], function(wangEditor){\n",
       "// createEditor, createToolbar, IEditorConfig, IDomEditor\n",
       "//【注意】下面使用的 typescript 语法。如用 javascript 语法，把类型去掉即可。\n",
       "\n",
       "// 编辑器配置\n",
       "const editorConfig = {}\n",
       "editorConfig.placeholder = '请输入内容'\n",
       "editorConfig.onChange = (editor) => {\n",
       "    // 当编辑器选区、内容变化时，即触发\n",
       "    console.log('content', editor.children)\n",
       "    console.log('html', editor.getHtml())\n",
       "}\n",
       "\n",
       "// 工具栏配置\n",
       "const toolbarConfig = {}\n",
       "\n",
       "// 创建编辑器\n",
       "const editor = wangEditor.createEditor({\n",
       "  selector: '#editor-container',\n",
       "  config: editorConfig,\n",
       "  mode: 'default' // 或 'simple' 参考下文\n",
       "})\n",
       "// 创建工具栏\n",
       "const toolbar = wangEditor.createToolbar({\n",
       "  editor,\n",
       "  selector: '#toolbar-container',\n",
       "  config: toolbarConfig,\n",
       "  mode: 'default' // 或 'simple' 参考下文\n",
       "})\n",
       "});\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<div id=\"toolbar-container\"></div>\n",
    "<div id=\"editor-container\"></div>\n",
    "<script>\n",
    "requirejs(['wangEditor'], function(wangEditor){\n",
    "// createEditor, createToolbar, IEditorConfig, IDomEditor\n",
    "//【注意】下面使用的 typescript 语法。如用 javascript 语法，把类型去掉即可。\n",
    "\n",
    "// 编辑器配置\n",
    "const editorConfig = {}\n",
    "editorConfig.placeholder = '请输入内容'\n",
    "editorConfig.onChange = (editor) => {\n",
    "    // 当编辑器选区、内容变化时，即触发\n",
    "    console.log('content', editor.children)\n",
    "    console.log('html', editor.getHtml())\n",
    "}\n",
    "\n",
    "// 工具栏配置\n",
    "const toolbarConfig = {}\n",
    "\n",
    "// 创建编辑器\n",
    "const editor = wangEditor.createEditor({\n",
    "  selector: '#editor-container',\n",
    "  config: editorConfig,\n",
    "  mode: 'default' // 或 'simple' 参考下文\n",
    "})\n",
    "// 创建工具栏\n",
    "const toolbar = wangEditor.createToolbar({\n",
    "  editor,\n",
    "  selector: '#toolbar-container',\n",
    "  config: toolbarConfig,\n",
    "  mode: 'default' // 或 'simple' 参考下文\n",
    "})\n",
    "});\n",
    "</script>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f5c5cf08",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h1>Classic editor</h1>\n",
       "<div id=\"editor\">\n",
       "    <p>This is some sample content.</p>\n",
       "</div>\n",
       "<script>\n",
       "    requirejs(['CKEditor'], function(ClassicEditor){\n",
       "    ClassicEditor\n",
       "        .create( document.querySelector( '#editor' ) )\n",
       "        .catch( error => {\n",
       "            console.error( error );\n",
       "        } );\n",
       "    });\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<h1>Classic editor</h1>\n",
    "<div id=\"editor\">\n",
    "    <p>This is some sample content.</p>\n",
    "</div>\n",
    "<script>\n",
    "    requirejs(['CKEditor'], function(ClassicEditor){\n",
    "    ClassicEditor\n",
    "        .create( document.querySelector( '#editor' ) )\n",
    "        .catch( error => {\n",
    "            console.error( error );\n",
    "        } );\n",
    "    });\n",
    "</script>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "77af56e4",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
